const PENDING = "pending"
const FULFILLED = "fulfilled"
const REJECTED = "rejected"
//1.基本结构 2.then方法  3.all方法  4.reace方法  5.resolve方法  6.reject方法  7.finally方法
class MyPromise {
  //状态 + 结果
  PromiseState = PENDING
  PromiseResult = undefined
  //保存两组回调函数
  fulfilledCbs = []
  rejectedCbs = []
  //构造方法(执行器函数)
  constructor(executor) {
    //处理异常
    try {
      //是怎么执行的?
      executor(this.resolve.bind(this), this.reject.bind(this))
    } catch (err) {
      this.reject(err)
    }
  }
  resolve(val) {
    if (this.PromiseState !== PENDING) return
    this.PromiseState = FULFILLED
    this.PromiseResult = val
    //成功后把保存成功的回调数组里面的函数一个个执行
    while (this.fulfilledCbs.length) {
      this.fulfilledCbs.shift()()
    }
  }
  reject(reason) {
    if (this.PromiseState !== PENDING) return
    this.PromiseState = REJECTED
    this.PromiseResult = reason
    while (this.rejectedCbs.length) {
      this.rejectedCbs.shift()()
    }
  }
  /*
  接收两个回调
  如果当前promise对象为成功状态，执行第一个回调
  如果当前promise对象为失败状态，执行第二个回调
  如果当前promise对象为待定状态，暂时保存两个回调

  then方法本身会返回一个新的promise对象，
  该对象的状态和结果由回调函数的返回值决定
  如果返回值是promise对象
        返回值为成功，新promise就是成功
        返回值为失败，新promise就是失败
  如果返回值非promise对象
        新promise就是成功，她的值就是返回值

  */
  //  成功和失败的回调       PromiseResult是上一次的结果
  then(onFulfilled, onRejected) {
    //判断传进来的是不是函数                                            如果不是一个函数把原先的值返回出去，是一个函数形式
    onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (val) => val
    onRejected =
      typeof onRejected === "function"
        ? onRejected
        : (reason) => {
            throw reason
          }
    //函数里面异步的异常，外层的catch try捕捉不到
    const thenPromise = new MyPromise((resolve, reject) => {
      const reasonPromise = (cb) => {
        queueMicrotask(() => {
          try {
            let x = cb(this.PromiseResult)
            //判断她有没有返回自己 let p2 = p1.then(()=> return p2)
            if (x === thenPromise) {
              throw new Error("不能返回自身...")
            }
            //判断x是不是promise
            if (x instanceof MyPromise) {
              x.then(resolve, reject)
            } else {
              resolve(x)
            }
          } catch (error) {
            reject(error)
          }
        })
      }
      if (this.PromiseState === FULFILLED) {
        reasonPromise(onFulfilled)
      } else if (this.PromiseState === REJECTED) {
        reasonPromise(onRejected)
      } else if (this.PromiseState === PENDING) {
        //如果是待定状态，就把成功和失败的回调保存起来
        //因为成功或者失败可以设置多个回调，所以用数组表示
        //并且可能需要上次返回的结果，作为下一次的参数。所以用bind传入返回一个新的函数
        // this.fulfilledCbs.push(onFulfilled.bind(this, this.PromiseResult))
        // this.rejectedCbs.push(onRejected.bind(this, this.PromiseResult))
        this.fulfilledCbs.push(reasonPromise.bind(this, onFulfilled))
        this.rejectedCbs.push(reasonPromise.bind(this, onRejected))
      }
    })
    return thenPromise
  }

  /*
        all是一个静态方法，需要一个数组作为参数
        参数数组中，如果所以promise对象都为成功，返回成功状态的promise
        参数数组中，只要有一个promise对象为失败，返回失败promise
    */
  static all(arr) {
    const result = [] //存放成功结果的数组
    let n = 0 //记录有几个成功
    return new MyPromise((resolve, reject) => {
      //1.返回一个promise，不管是成功还是失败
      const addData = (index, val) => {
        result[index] = val
        n++
        if (n === result.length) {
          resolve(result)
        }
      }
      arr.forEach((item, index) => {
        //2.循环传入的数组 判断是不是promise对象，不是的话映射到数组里面
        if (item instanceof MyPromise) {
          //判断当前项是不是一个promise
          item.then((val) => {
            //如果是的话，在成功回调和失败回调中做对应操作，
            addData(index, val)
            // result[index] = val
            // n++
            // if (n === result.length) {
            // 	resolve(result)
            // }
          }, reject)
        } else {
          addData(index, item)
          // result[index] = item
          // n++
          // if (n === result.length) {
          // 	resolve(result)
          // }
        }
      })
    })
  }

  /*
    race是一个静态方法，需要一个数组作为参数
    返回是一个promise
    数组中的字面量，被视为成功的promise
    promise的状态和结果，由参数数组中最快得到的结果决定
    */
  static race(arr) {
    return new MyPromise((resolve, reject) => {
      arr.forEach((item) => {
        if (item instanceof MyPromise) {
          item.then(resolve, reject)
        } else {
          queueMicrotask(() => {
            //如果不加一个异步的话，他就是同步的，而promise.then是异步的，那么这个字面量成功的速度会快于promise.then()。
            resolve(item)
          })
        }
      })
    })
  }

  /*
   resolve是一个静态方法

        let p1 = new Promise.reject(123)
        let p2 = new Promise.resolve(p1)
        console.log(p2)   //rejected  
        console.log(p1 === p2) //true
        reslove的参数是字面量的话，他就会原封不动的返回
        是promise的话，他就会返回原来promise的结果，是reject就返回reject，是resolve就返回resolve

   */
  static resolve(val) {
    if (val instanceof MyPromise) return val
    return new MyPromise((resolve) => resolve(val))
  }
  /*    
  reject是一个静态方法
  不管是什么，都会被一个失败的promise包裹住
  let p1 = new Promise.reject(Promise.resolve(123))
  let p2 = new Promise.reject(Promise.reject(123))
  let p3 = new Promise.reject(123)
  console.log(p1) //失败对象，里面的结果是一个成功的promise
  console.log(p2) //失败对象，里面的结果是一个失败的promise
  console.log(p3) //失败对象，里面的结果 123
  */
  static reject(val) {
    return new MyPromise((reject) => reject(val))
  }
  /*
  finally方法
        ①            ②            ③
    Promise.finally( 回调函数 ).then(...)
        1.执行的顺序 ① ② ③
        2.最终的结果
            回调函数成功--》then(promise的结果)
            回调函数失败--》then(回调的失败)
    Promise.resolve('成功').finally(() => '成功-成功').then(console.log,null) //成功
    Promise.reject('失败').finally(() => '失败-成功').then(null,console.log) //失败
    Promise.resolve('成功').finally(() =>  Promise.resolve('成功-成功')).then(console.log,null) //成功
    Promise.reject('失败').finally(() => Promise.resolve('失败-成功')).then(null,console.log)//失败

    Promise.resolve('成功').finally(() => {thorw '成功-失败'}).then(null,console.log)//成功-失败
    Promise.reject('失败').finally(() => {thorw '失败-失败'}).then(null,console.log)//失败-失败
    Promise.resolve('成功').finally(() => Promise.reject('成功-失败')).then(null,console.log)//成功-失败
    Promise.reject('失败').finally(() => Promise.reject('失败-失败')).then(null,console.log)//失败-失败
  */
  finally(callback) {
    //这里可能需要理解一些
    //     this就是外面的promise,由于不知道是成功还是失败所以在第一个then当中失败和成功的回调都是一样的，就保证了finally()里面的函数的执行
    //                                       如果成功的话返回的this指向的是外面，也就是外面的Promise
    return this.then(callback, callback).then(this, (err) => {
      throw err
    })
  }
  /*
  catch
  */
  catch(onRejected) {
    return this.then(null, onRejected)
  }
}
